<?php
/**
 * Desc 数据库连接池 spl+swoole
 * User: wangshuai
 * Date: 2021/8/30
 * Time: 15:35
 * Email: <1076890579@qq.com>
 */

class Pool
{
    //数据库连接池
    protected $connections;
    //最大连接数
    protected $max;
    //最小连接数
    protected $min;
    //已连接数
    protected $count;
    //是否初始化
    protected $inited = false;
    //单例
    private static $instance;
    //数据库配置
    protected $config = [
        'host' => '127.0.0.1',
        'port' => 3306,
        'user' => 'root',
        'password' => '',
        'database' => 'vue_book',
        'charset' => 'utf8',
        'timeout' => 2,
    ];
    public function __construct() {
        //初始化一个队列
        $this->connections = new SplQueue();
        $this->max = 30;
        $this->min = 5;

        //绑定单例
        self::$instance = & $this;
    }

    /**
     * 进程开启 建立最小连接数
     */
    public function init() {
        if ($this->inited) {
            return null;
        }
        for ($i = 0; $i < $this->min; $i++) {
            $this->generate();
        }
        return $this;
    }
    /**
     * 维持当前的连接数不断线，并且剔除断线的链接
     */
    public function keepAlive() {
        //2分钟检查一次
        Swoole\Timer::tick(120000, function () {
            //维持连接
            while ($this->connections->count() > 0) {
                $next = $this->connections->shift();
                $next->query('SELECT 1');
                echo "当前连接数：" . $this->connections->count() . PHP_EOL ;
                $this->connections->push($next);
            }
        });
        //维持活跃数在min到max之间
        Swoole\Timer::tick(1000, function () {
            if ($this->connections->count() > $this->max) {
                while ($this->max < $this->connections->count()) {
                    $next = $this->connections->shift();
                    $next->close();
                    $this->count--;
                    echo "关闭连接...\n" ;
                }
            }
        });
    }
    /**
     * 建立一个新的连接
     */
    public function generate($callback = null) {
        $db = new Swoole\Coroutine\MySQL();
        $db->connect($this->config);
        $this->count++;
        $this->addConnections($db);
        if (is_callable($callback)) {
            call_user_func($callback);
        }
    }

    /**
     * 连接推进队列
     * @param $db
     * @return $this
     */
    public function addConnections($db) {
        $this->connections->push($db);
        return $this;
    }
    /**
     * 执行数据库命令 . 会判断连接数够不够，够就直接执行，不够就新建连接执行
     */
    public function query($query) {
        if($this->connections->count() == 0) {
            $this->generate(function() use ($query) {
                return $this->exec($query);
            });
        } else {
            return $this->exec($query);
        }
    }

    /**
     * 直接执行数据库命令并且 callback();
     * @param $query
     */
    private function exec($query) {
        if ($this->connections->isEmpty()) {
            $db = new Swoole\Coroutine\MySQL();
            $db->connect($this->config);
            $this->connections->push($db);
        } else {
            $db = $this->connections->shift();
        }
        $res = $db->query($query);
        return $res;
    }

    /**
     * 单例
     * @return mixed
     */
    public static function getInstance() {
        if (is_null(self::$instance)) {
            self::$instance = new Pool();
        }
        return self::$instance;
    }
}
$server = new Swoole\Http\Server('127.0.0.1', 9501);
$server->set([
    'worker_num' => 4,
]);
$server->on("WorkerStart", function($server , $wid){
    Pool::getInstance()->init()->keepAlive();
});
$server->on("request", function($request, $response){
    $res = Pool::getInstance()->query('SELECT * FROM ba_user');
    $response->end(json_encode($res));
});
$server->start();